﻿using AutoUpdater.Models;
using AutoUpdater.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.IO;
using System.Net;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

namespace AutoUpdater
{
    internal delegate void MAction();
    internal class Common
    {
        /// <summary>
        /// 默认配置清单文件名
        /// </summary>
        public static readonly string ManifestName = "AutoUpdateMaps.xml";

        #region 启动器配置属性
        /// <summary>
        /// 启动器配置文件名
        /// </summary>
        public static string UpdateIniFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AutoUpdate.ini");
        /// <summary>
        /// 初始化标记
        /// </summary>
        public static bool InitLocked
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "InitLocked", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "InitLocked", "0", true, Common.UpdateIniFile);
                    return false;
                }
                return rtn == "1";
            }
            set
            {
                var str = value ? "1" : "0";
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "InitLocked", str, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 使用简化窗体
        /// </summary>
        public static bool UseFormLite
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "UseFormLite", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "UseFormLite", "0", true, Common.UpdateIniFile);
                    return false;
                }
                return rtn == "1";
            }
            set
            {
                var str = value ? "1" : "0";
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "UseFormLite", str, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 配置文件地址
        /// </summary>
        public static string ConfigUrl
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "ConfigUrl", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    rtn = "http://";
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "ConfigUrl", rtn, true, Common.UpdateIniFile);
                }
                return rtn;
            }
            set
            {
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "ConfigUrl", value, true, Common.UpdateIniFile);
            }
        }

        /// <summary>
        /// 主程序版本
        /// </summary>
        public static string AppVersion
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AppVersion", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    rtn = "";
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppVersion", rtn, true, Common.UpdateIniFile);
                }
                return rtn;
            }
            set
            {
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppVersion", value, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 主程序更新目录
        /// </summary>
        public static string AppRootPath
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AppRootPath", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    rtn = System.AppDomain.CurrentDomain.BaseDirectory;
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRootPath", rtn, true, Common.UpdateIniFile);
                }
                return TrailingPath(rtn);
            }
            set
            {
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRootPath", value, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 主程序更新缓存目录
        /// </summary>
        public static string AppRootTmp
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AppRootTmp", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    rtn = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "uptemp");
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRootTmp", rtn, true, Common.UpdateIniFile);
                }
                return TrailingPath(rtn);
            }
            set
            {
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRootTmp", value, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 启动器绝对路径
        /// </summary>
        public static string AppLaunchExe
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AppLaunchExe", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    rtn = "";
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppLaunchExe", rtn, true);
                }
                return rtn;
            }
            set
            {
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppLaunchExe", value, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 启动自动更新
        /// </summary>
        public static bool AutoUpdate
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AutoUpdate", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AutoUpdate", "0", true, Common.UpdateIniFile);
                    return false;
                }
                return rtn == "1";
            }
            set
            {
                var str = value ? "1" : "0";
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AutoUpdate", str, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 自启动主程序
        /// </summary>
        public static bool AppRestart
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AppRestart", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRestart", "0", true, Common.UpdateIniFile);
                    return false;
                }
                return rtn == "1";
            }
            set
            {
                var str = value ? "1" : "0";
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AppRestart", str, true, Common.UpdateIniFile);
            }
        }
        /// <summary>
        /// 自动关闭更新程序
        /// </summary>
        public static bool AutoClose
        {
            get
            {
                var rtn = ConfHelper.ReadValue(ConfHelper.SECTION_SETTINGS, "AutoClose", true, Common.UpdateIniFile);
                if (string.IsNullOrEmpty(rtn))
                {
                    //ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AutoClose", "0", true, Common.UpdateIniFile);
                    return false;
                }
                return rtn == "1";
            }
            set
            {
                var str = value ? "1" : "0";
                ConfHelper.WriteValue(ConfHelper.SECTION_SETTINGS, "AutoClose", str, true, Common.UpdateIniFile);
            }
        }
        #endregion

        #region 通用处理函数
        /// <summary>
        /// 文件绝对路径补全
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string TrailingFilePath(string path)
        {
            var flag = !String.IsNullOrEmpty(path?.Trim()) && path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) == -1 && Path.IsPathRooted(path) && !Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal);
            return flag ? path : TrailingPath(AppDomain.CurrentDomain.BaseDirectory) + path;
        }
        /// <summary>
        /// 绝对路径补全
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string TrailingPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return path;
            return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
        }

        /// <summary>
        /// 判断是否http地址
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsHttpAddress(string input)
        {
            if (string.IsNullOrEmpty(input?.Trim())) return false;
            return System.Text.RegularExpressions.Regex.IsMatch(input, @"^(http(s)?://)?(localhost|[\d.]+|\w+(\.\w+)*)(:\d+)?(/[\w-./?%&=]*)?$");
        }
        /// <summary>
        /// 判断是否IP地址
        /// </summary>
        /// <param name="input">待判断的IP地址</param>
        /// <returns></returns>
        public static bool IsIPAddress(string input)
        {
            if (string.IsNullOrEmpty(input?.Trim())) return false;
            return System.Text.RegularExpressions.Regex.IsMatch(input, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
        }
        /// <summary>
        /// 获取指令
        /// </summary>
        /// <param name="args"></param>
        /// <param name="cmd"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool TryGetCommand(string[] args, string cmd, out bool t)
        {
            t = false;
            if (args != null)
            {
                for (int i = 0; i < args.Length; i++)
                {
                    if (!string.IsNullOrEmpty(args[i]))
                    {
                        if (args[i].StartsWith(cmd) && args[i].IndexOf('=') > -1)
                        {
                            return Boolean.TryParse(args[i].Substring(args[i].IndexOf('=') + 1), out t);
                        }
                        else if (args[i].StartsWith(cmd))
                        {
                            t = true;
                            return true;
                        }
                    }

                }
            }
            return false;
        }
        /// <summary>
        /// 获取指令
        /// </summary>
        /// <param name="args"></param>
        /// <param name="cmd"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static bool TryGetCommand(string[] args, string cmd, out string t)
        {
            t = string.Empty;
            if (args != null)
            {
                for (int i = 0; i < args.Length; i++)
                {

                    if (!string.IsNullOrEmpty(args[i]))
                    {
                        if (args[i].StartsWith(cmd) && args[i].IndexOf('=') > -1)
                        {
                            t = args[i].Substring(args[i].IndexOf('=') + 1);
                            return true;
                        }
                        else if (args[i].StartsWith(cmd))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        #endregion

        #region 进程操作函数
        /// <summary>
        /// 进程是否正在运行
        /// </summary>
        /// <param name="launchPath"></param>
        /// <returns></returns>
        public static bool IsProcessRunning(string launchPath)
        {
            string processName = System.IO.Path.GetFileNameWithoutExtension(launchPath);
            Process[] processes = Process.GetProcessesByName(processName);

            foreach (Process process in processes)
            {
                if (process.MainModule.FileName.Equals(launchPath, StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine("IsRunning:" + processName);
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// 获取与当前进程相同的正在运行的实例，没有运行的实例返回null;
        /// </summary>
        public static Process ProcessInstance(out string err)
        {
            try
            {
                err = string.Empty;
                Process current = Process.GetCurrentProcess();
                Process[] processes = Process.GetProcessesByName(current.ProcessName);

                foreach (Process process in processes)
                {
                    if (process.Id != current.Id)
                    {
                        if (process.MainModule.FileName == current.MainModule.FileName)
                        {
                            return process;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                err = ex.Message + "\r\n" + ex.StackTrace;
            }
            return null;
        }
        /// <summary>
        /// 结束指定路径程序
        /// </summary>
        /// <param name="launchPath"></param>
        /// <exception cref="Exception"></exception>
        public static bool CloseProcess(string launchPath, out string err)
        {
            try
            {
                err = string.Empty;
                Process[] processes = Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(launchPath));
                foreach (Process process in processes)
                {
                    if (!process.HasExited)
                    {
                        process.CloseMainWindow(); // 尝试关闭主窗口
                        process.WaitForExit(5000); // 等待程序退出，最多等待5秒钟
                    }

                    if (!process.HasExited)
                    {
                        process.Kill(); // 如果程序仍未退出，强制终止进程
                        process.WaitForExit(5000); // 等待程序退出，最多等待5秒钟
                    }
                }
            }
            catch (Exception ex)
            {
                err = "关闭外部程序时发生错误：" + ex.Message;
                return false;
                //throw new Exception("关闭外部程序时发生错误：" + ex.Message);
            }
            return true;
        }
        /// <summary>
        /// 运行指定路径程序
        /// </summary>
        /// <param name="name"></param>
        public static bool StartProcess(string launchPath, out string err)
        {
            try
            {
                err = string.Empty;
                if (string.IsNullOrEmpty(launchPath)) return false;
                System.Diagnostics.ProcessStartInfo process = new System.Diagnostics.ProcessStartInfo();
                process.FileName = launchPath;
                System.Diagnostics.Process prc;
                prc = System.Diagnostics.Process.Start(process);
            }
            catch (Exception ex)
            {
                err = "运行外部程序时发生错误：" + ex.Message;
                return false;
            }
            return true;
        }

        /// <summary>
        /// 该函数设置由不同线程产生的窗口的显示状态。
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="cmdShow">指定窗口如何显示。查看允许值列表，请查阅ShowWlndow函数的说明部分。</param>
        /// <returns>如果函数原来可见，返回值为非零；如果函数原来被隐藏，返回值为零。</returns>
        [DllImport("User32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        /// <summary>
        /// 该函数将创建指定窗口的线程设置到前台，并且激活该窗口。键盘输入转向该窗口，并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。
        /// </summary>
        /// <param name="hWnd">将被激活并被调入前台的窗口句柄。</param>
        /// <returns>如果窗口设入了前台，返回值为非零；如果窗口未被设入前台，返回值为零。</returns>
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary>
        /// 显示已运行的程序。
        /// </summary>
        public static void HandleRunningInstance(Process instance)
        {
            ShowWindowAsync(instance.MainWindowHandle, 1); //显示
            SetForegroundWindow(instance.MainWindowHandle); //放到前端
        }
        #endregion

        #region 核心操作函数
        /// <summary>
        /// 清单文件链接补全
        /// </summary>
        /// <param name="url">文件父目录</param>
        /// <returns></returns>
        public static string FormatManifestUrl(string url)
        {
            var configurl = Common.ConfigUrl;
            if (IsHttpAddress(url))
            {
                if(url.ToLower().LastIndexOf(".xml") > 0)
                    configurl = url;
                else
                    configurl = url + "/" + Common.ManifestName;
            }
            return new Uri(configurl).ToString();
        }

        ///<summary>
        /// HTTP读取文件文本内容
        /// </summary>
        /// <param name="fileUrl">文件链接地址</param>
        public static string DownloadAutoUpdateMapsXmlText(string fileUrl)
        {
            HttpWebRequest resq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(fileUrl);
            HttpWebResponse resp = (System.Net.HttpWebResponse)resq.GetResponse();
            //判断服务器未响应
            if (resp.StatusCode != HttpStatusCode.OK)
            {
                throw new Exception("文件内容地址响应失败");
            }
            Stream st = resp.GetResponseStream();
            Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
            StreamReader reader = new StreamReader(st, encode);
            return reader.ReadToEnd();

            //byte[] respDatas = HttpHelper.HttpGet(fileUrl, null, false, 60, "application/xml", null);
            //return respDatas != null ? UTF8Encoding.UTF8.GetString(respDatas, 0, respDatas.Length) : null;
        }

        /// <summary>
        /// 读取并解析XML到对象
        /// </summary>
        /// <param name="targetXml"></param>
        public static UptBaseModel ReadAutoUpdateMapsXml(string fileUrl)
        {
            UptBaseModel modelData = new UptBaseModel()
            {
                version = "1.0.0.0",
                description = "",
                files = new List<FileInfoModel>(),
            };

            if (string.IsNullOrEmpty(fileUrl)) return modelData;

            string targetXml = DownloadAutoUpdateMapsXmlText(fileUrl);
            if (string.IsNullOrEmpty(targetXml)) return modelData;

            var tempModel = XmlHelper.Deserialize<UptBaseModel>(targetXml);
            if (tempModel != null)
            {
                modelData.version = tempModel.version;
                modelData.description = tempModel.description;
                modelData.server = EncryptParse.DESDecrypt(tempModel.server ?? "", EncryptParse.GenerateDESKey(tempModel.version ?? ""));
                if (tempModel.files != null && tempModel.files.Count > 0)
                {
                    modelData.count = tempModel.files.Count;
                    foreach (var item in tempModel.files)
                    {
                        //DES解密信息
                        item.fileName = EncryptParse.DESDecrypt(item.fileName, EncryptParse.GenerateDESKey(item.hash1));
                        item.filePath = EncryptParse.DESDecrypt(item.filePath, EncryptParse.GenerateDESKey(item.hash1));
                        item.fileDrect = EncryptParse.DESDecrypt(item.fileDrect, EncryptParse.GenerateDESKey(item.hash1));
                        item.urlDrect = EncryptParse.DESDecrypt(item.urlDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));

                        var toPath = Common.AppRootPath + item.fileDrect;
                        //比对文件MD5，相同不更新
                        if (File.Exists(toPath))
                        {
                            var nowHash = EncryptParse.GetFileMD5Hash(toPath);
                            if (nowHash == item.hash1)
                            {
                                item.loadprogress = "已最新";
                            }
                            else
                            {
                                modelData.totalFileSize += item.fileBytes;
                            }
                        }
                        else
                        {
                            modelData.totalFileSize += item.fileBytes;
                        }
                    }

                    modelData.files.Clear();
                    modelData.files.AddRange(tempModel.files);
                }
            }
            return modelData;
        }

        /// <summary>
        /// 创建下载任务
        /// </summary>
        /// <param name="modelBase"></param>
        /// <param name="tempPath"></param>
        /// <param name="downloadProgressChangedEventHandler"></param>
        /// <param name="asyncCompletedEventHandler"></param>
        public static int CreatDownloadTasks(UptBaseModel modelBase, string tempPath, DownloadProgressChangedEventHandler downloadProgressChangedEventHandler = null, AsyncCompletedEventHandler asyncCompletedEventHandler = null)
        {
            if (modelBase != null && modelBase.files != null && modelBase.files.Count > 0)
            {
                if (string.IsNullOrEmpty(tempPath)) 
                    tempPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "uptemp"); //默认文件缓存路径

                tempPath = TrailingPath(tempPath);
                //缓存目录文件夹不存在则创建
                if (Directory.Exists(tempPath) == false)
                {
                    Directory.CreateDirectory(tempPath);
                }

                foreach (var item in modelBase.files)
                {
                    item.targetPath = Common.AppRootPath + item.fileDrect;
                    
                    //比对文件MD5，相同不更新
                    if (File.Exists(item.targetPath))
                    {
                        var nowHash = EncryptParse.GetFileMD5Hash(item.targetPath);
                        if (nowHash == item.hash1) continue;
                    }

                    //临时缓存目录
                    var cachePath = tempPath + item.fileDrect;
                    //目标文件夹不存在则创建
                    if (Directory.Exists(Path.GetDirectoryName(cachePath)) == false)
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
                    }
                    //开始创建连接下载
                    Uri uri = new Uri(item.urlDrect);
                    WebClient client = new WebClient();
                    client.UseDefaultCredentials = true;
                    if(uri.Scheme.ToLower() == "ftp") client.Credentials = new NetworkCredential("guest", ""); // 替换为FTP服务器的用户名和密码
                    if (downloadProgressChangedEventHandler != null) client.DownloadProgressChanged += downloadProgressChangedEventHandler;
                    if (asyncCompletedEventHandler != null) client.DownloadFileCompleted += asyncCompletedEventHandler;
                    client.DownloadFileAsync(uri, cachePath, item);

                    modelBase.updateCount++;
                }
            }
            return modelBase.updateCount;
        }

        /// <summary>
        /// 开始执行更新及启动目标应用程序
        /// </summary>
        /// <param name="modelData"></param>
        /// <param name="rootPath"></param>
        /// <param name="tempPath"></param>
        /// <param name="appLaunchExe"></param>
        /// <param name="isAppRestart"></param>
        /// <param name="isbackup"></param>
        /// <param name="form"></param>
        public static void StartUpgradeProcess(UptBaseModel modelData, string rootPath, string tempPath, string appLaunchExe, bool isAppRestart = false, bool isAutoClose = false, bool isbackup = false, Form form = null)
        {
            if (modelData != null && !string.IsNullOrEmpty(rootPath) && !string.IsNullOrEmpty(tempPath))
            {
                //开启更新线程
                var thread = new System.Threading.Thread(() =>
                {
                    //强制关闭主程序
                    //if (Common.IsProcessRunning(appLaunchExe)) Common.CloseProcess(appLaunchExe, out string err);

                    //备份文件目录
                    var backuppath = isbackup ? TrailingPath(rootPath) + "bak_" + DateTime.Now.ToString("yyyyMMdd") : null;
                    //更新覆盖目标应用文件
                    FileOperate.CopyDireToDire(TrailingPath(tempPath), TrailingPath(rootPath), TrailingPath(backuppath), true);
                    //清空临时文件夹
                    FileOperate.DeleteFilesFromFolder(tempPath);

                    //记录当前主版本号
                    Common.AppVersion = modelData.version;

                    //是否自动重启主程序
                    if (isAppRestart && !string.IsNullOrEmpty(appLaunchExe) && !Common.IsProcessRunning(appLaunchExe))
                    {
                        Common.StartProcess(appLaunchExe, out string err);
                    }

                    //是否自动关闭更新器
                    if (isAutoClose && form != null)
                    {
                        form.Invoke(new MAction(() => { form.Close(); }));
                    }

                });
                thread.IsBackground = true;
                thread.Start();
            }

        }
        #endregion

    }
}
